package com.kingsoft.minibase;

import com.kingsoft.minibase.MiniBase.*;
import com.kingsoft.minibase.DiskFile.*;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DiskStore {
    private static String FILE_NAME_TMP_SUFFIX = ".tmp";
    private static final String FILE_NAME_ARCHIVE_SUFFIX = ".archive";
    private static final Pattern DATA_FILE_RE = Pattern.compile("data\\.([0-9]+)"); // data.1

    private String dataDir;
    private volatile AtomicLong maxFileID;
    private List<DiskFile> diskFiles;
    // 从配置文件获取，用于compact的判断
    private int maxDiskFiles;

    public DiskStore(String dataDir, int maxDiskFiles) {
        this.dataDir = dataDir;
        this.diskFiles = new ArrayList<>();
        this.maxDiskFiles = maxDiskFiles;
    }

    public File[] getListFiles() {
        File file = new File(this.dataDir);
        // 返回目录下所有匹配的文件
        return file.listFiles(fname -> DATA_FILE_RE.matcher(fname.getName()).matches());
    }



    public void open() throws IOException {
        // 启动的时候需要加载所有以前已经刷盘的文件信息，用列表缓存
        File[] files = getListFiles();
        for (File file : files) {
            DiskFile diskFile = new DiskFile();
            diskFile.open(file.getAbsolutePath());
            diskFiles.add(diskFile);
        }
        maxFileID = new AtomicLong(getMaxFileID());
    }

    private synchronized long getMaxFileID() {
        File[] files = getListFiles();
        long maxFileId = -1L;
        for (File f : files) {
            Matcher matcher = DATA_FILE_RE.matcher(f.getName());
            if (matcher.matches()) {
                maxFileId = Math.max(Long.parseLong(matcher.group(1)), maxFileId);
            }
        }
        return maxFileId;
    }

    public static class DefaultFlusher implements Flusher {
        private DiskStore diskStore;
        public DefaultFlusher(DiskStore diskStore) {
            this.diskStore = diskStore;
        }

        @Override
        public void flush(Iter<KeyValue> iter) throws IOException {
            // 获取磁盘文件名
            String fileName = diskStore.getNextDiskFileName();
            String tmpFilename = fileName + FILE_NAME_TMP_SUFFIX;
            try {
                // 开始写入临时文件，获取DiskFileWriter对象，先将dataBlock写满，在写indexBlock和trailBlock
                try(DiskFileWriter diskFileWriter = new DiskFileWriter(tmpFilename)) {
                    // 将内存数据写入diskFileWriter，每写完一个数据块之后就更新一次file的index块和的trailer部分信息
                    while (iter.hasNest()) {
                        diskFileWriter.append(iter.next());
                    }
                    diskFileWriter.appendIndex();
                    diskFileWriter.appendTrailer();
                }
                File f = new File(tmpFilename);
                // 如果rename失败说明还在flush中
                if (!f.renameTo(new File(fileName))) {
                    throw new IOException(
                            "Rename " + tmpFilename + " to " + fileName + " failed when flushing");
                }
                //记录已经刷写的文件，方便后续操作吗比如compact
                diskStore.addDiskFile(fileName);
            } finally {
                // 删除临时文件
                File file = new File(tmpFilename);
                if (file.exists()) {
                    file.delete();
                }
            }
        }
    }

    public static class DefaultCompactor extends Compactor {

        @Override
        public void compact() throws IOException {

        }

        @Override
        public void run() {
            super.run();
        }
    }

    private void addDiskFile(String fileName) throws IOException {
        DiskFile diskFile = new DiskFile();
        diskFile.open(fileName);
        addDiskFile(diskFile);
    }

    private void addDiskFile(DiskFile diskFile) {
        synchronized (diskFiles) {
            diskFiles.add(diskFile);
        }
    }

    public synchronized String getNextDiskFileName() {
        // 将整数格式化为一个包含20位数字的字符串
        return new File(this.dataDir, String.format("data.%020d", nextDiskFileId())).toString();
    }

    private synchronized long nextDiskFileId() {
        return maxFileID.incrementAndGet();
    }


}
